﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using JVMTI_CONN_CONNECT;
using System.Diagnostics;
using System.Windows.Controls.Primitives;

namespace JVMTI_CORE_SHELL
{
    /// <summary>
    /// Interaction logic for LifeEventVisualizer.xaml
    /// </summary>
    public partial class LifeEventVisualizer : UserControl
    {
        public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(ILifeEvent), typeof(LifeEventVisualizer), new PropertyMetadata(null));
        //static double RectangleQuantityCount = App.MaximumDrawingWidth / MinimumRectangleWidth;
        const double MinimumRectangleWidth = 20;
        static SolidColorBrush WhiteFill = new SolidColorBrush(Colors.White);
        static SolidColorBrush RedFill = new SolidColorBrush(Colors.Red);
        static SolidColorBrush DeeperAccentBrush = App.Current.Resources["DeeperAccentBrush"] as SolidColorBrush;
        static SolidColorBrush AccentBrush = App.Current.Resources["AccentBrush"] as SolidColorBrush;

        public ILifeEvent SelectedValue
        {
            get { return this.GetValue(SelectedValueProperty) as ILifeEvent; }
            set { this.SetValue(SelectedValueProperty, value); }
        }

        public LifeEventVisualizer()
        {
            this.InitializeComponent();
            this.SizeChanged += LifeEventVisualizer_SizeChanged;
            this.DataContextChanged += LifeEventVisualizer_DataContextChanged;
            this.MouseWheel += LifeEventVisualizer_MouseWheel;
        }

        void LifeEventVisualizer_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            var pt = e.GetPosition(TrueRoot);
            var scale = TrueRoot.RenderTransform as ScaleTransform;
            scale.CenterX = pt.X;
            scale.CenterY = pt.Y;
            scale.ScaleX += e.Delta / 50;
            if (scale.ScaleX < 1) scale.ScaleX = 1;
        }

        void LifeEventVisualizer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (DataContext != null && App.MaximumDrawingWidth > 0)
                Redraw();
        }

        void LifeEventVisualizer_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (DataContext != null && App.MaximumDrawingWidth > 0)
                Redraw();
        }
        bool ignoreResizeEvent = false;
        private void Redraw()
        {
            ignoreResizeEvent = true;
            var thread = (LifeCycleViewModel)DataContext;
            TrueRoot.Children.Clear();
            double maximumLength = App.MaximumDrawingWidth;
            //if (maximumLength < 640) maximumLength = 640;
            double maximumThreadLongevity = App.MaximumThreadEndTime - App.MinimumStartTime;
            if (maximumThreadLongevity <= 0 || maximumLength < 0) return;
            double minimumStartTime = App.MinimumStartTime;
            //return;
            StackPanel panel = new StackPanel() { Orientation = Orientation.Horizontal, Margin = new Thickness(5) };
            panel.Height = 15;
            double timeSlice = 0.01 * (thread.EndTimeStamp - thread.StartTimeStamp); //(thread.EndTimeStamp - thread.StartTimeStamp) / (maximumLength / MinimumRectangleWidth);
            // panel.Children.Add(new TextBlock() { Text = maximumLength.ToString(), Width = 100 });
            double drawRatio = maximumLength / maximumThreadLongevity;
            double marginLeft = (thread.StartTimeStamp - minimumStartTime) * drawRatio;
            //slice into 100 pieces
            //var accumulator = new Queue<int>();
            //var queueSum = 0.0;
            int numberOfEventInLastRectangle = 1;
            //sliding window
            //var slidingWindow = new Queue<int>();
            //slidingWindow.Enqueue(0);
            int j = 0;
            //first window 0-j.
            for (int i = 0; i < thread.KeyEvents.Count - 1; i++)
            {
                int jOrig = j;
                while (j < thread.KeyEvents.Count - 1 && thread.KeyEvents[j + 1].Timestamp - thread.KeyEvents[i].Timestamp < timeSlice)
                {
                    j++;//move window.
                }
                if (j != jOrig) { j--; }
                //j now points to the last index 
                // if i meets j, then there must be one event that is too large. This one will be drawn individually.
                //if average event size less than 5, we should collapse. Each slice is of timeSlice * drawRatio long.
                if ((thread.KeyEvents[j].Timestamp - thread.KeyEvents[i].Timestamp) * drawRatio < MinimumRectangleWidth * (j - i))
                {
                    //collapse
                    var segmentRect = new Rectangle();
                    if (i == 0)
                        segmentRect.Margin = new Thickness(marginLeft, 0, 0, 0);
                    segmentRect.Fill = DeeperAccentBrush;
                    if (drawRatio * (thread.KeyEvents[j].Timestamp - thread.KeyEvents[i].Timestamp) < 0)
                    {
                        Debugger.Launch();
                    }
                    segmentRect.Width = drawRatio * (thread.KeyEvents[j].Timestamp - thread.KeyEvents[i].Timestamp);
                    segmentRect.ToolTip = string.Format("Heavily contended time slice, {0} events collapsed.", (j - i));
                    segmentRect.Stroke = DeeperAccentBrush;
                    if (numberOfEventInLastRectangle > 1)
                    {
                        var lastPiece = panel.Children[panel.Children.Count - 1] as Rectangle;
                        panel.Children.RemoveAt(panel.Children.Count - 1);
                        segmentRect.Width += lastPiece.Width;
                        segmentRect.Margin = lastPiece.Margin;
                        segmentRect.ToolTip = string.Format("Heavily contended time slice, {0} events collapsed", (j - i) + numberOfEventInLastRectangle);
                        numberOfEventInLastRectangle = j - i + numberOfEventInLastRectangle;
                    }
                    else
                    {
                        // panel.Children.Add(segmentRect);
                        numberOfEventInLastRectangle = j - i + 1;
                        //segmentRect.ToolTip = string.Format("{0}-{1}", i, j);
                    }
                    segmentRect.DataContext = null;
                    panel.Children.Add(segmentRect);
                    i = j - 1;
                }
                else
                { //draw from i to j.
                    var lifeEvent = thread.KeyEvents[i];
                    var nextEvent = thread.KeyEvents[i + 1];
                    var segmentRect = new Rectangle() { Fill = lifeEvent.EmpahsizingEvent() ? WhiteFill : AccentBrush };
                    segmentRect.Fill = lifeEvent.HighlightingEvent ? RedFill : segmentRect.Fill;
                    if (i == 0)
                    {
                        segmentRect.Margin = new Thickness(marginLeft, 0, 0, 0);
                    }
                    segmentRect.Stroke = AccentBrush;
                    segmentRect.Width = (nextEvent.Timestamp - lifeEvent.Timestamp) * drawRatio;
                    PropertyBags prop = PropertyBags.ConvertToPropertyBags(lifeEvent, false);
                    prop.Add(new PropertyBag() { PropertyName = "Sequence", Value = i });
                    segmentRect.ToolTip = new ListBox() { HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch, ItemTemplate = (DataTemplate)App.Current.Resources["qiLstbox"], ItemsSource = prop };
                    if (segmentRect.Width != 0)
                    {
                        segmentRect.DataContext = lifeEvent;
                        panel.Children.Add(segmentRect);
                    }
                    numberOfEventInLastRectangle = 1;
                }
                if (j <= i) j = i + 1;
            }
            TrueRoot.Children.Add(panel);
            foreach (var uiElement in panel.Children)
            {
                ((Rectangle)uiElement).MouseLeftButtonDown += LifeEventVisualizer_MouseLeftButtonDown;
            }
            ignoreResizeEvent = false;
        }

        public void Highlight(IEnumerable<ILifeEvent> events)
        {
            var stkpnl = this.TrueRoot.Children[0] as StackPanel;
            var rectanges = stkpnl.Children.Cast<Rectangle>().Where<Rectangle>(rect =>
                {
                    var lifeEvent = rect.DataContext as ILifeEvent;
                    if (lifeEvent != null && events.Contains(lifeEvent))
                    {
                        return true;
                    }
                    return false;
                });
            foreach (var rect in rectanges)
            {
                rect.Fill = RedFill;
            }
        }

        void LifeEventVisualizer_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectedValue = (ILifeEvent)((sender as Rectangle).DataContext);
            //e.Handled = true;
        }
    }
}